﻿using KaroglanSharedLib;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using WK.Libraries.BetterFolderBrowserNS;

namespace KaroglanVersionProfiler
{
    public partial class KaroglanVersionProfiler : Form
    {
        public KaroglanVersionProfiler()
        {
            InitializeComponent();
            
            this.StartPosition = FormStartPosition.CenterScreen;
        }

        private Config cfg;

        private List<string> files = new List<string>();

        private HashSet<string> relativePaths = new HashSet<string>();

        private bool isAnalysis = false;

        Queue<Thread> activeThread = new Queue<Thread>();

        private void Form1_Load(object sender, EventArgs e)
        {
            Init();
        }

        private void Init()
        {
            cfg = Config.Load();
            //初始化窗口控件
            this.Text = $"卡洛兰版本分析工具 - ver{cfg.version}";
            cb_ClearAfterGenerate.Checked = cfg.clearDirectoryAfterGenerate;
            cb_OpenExplore.Checked = cfg.openExploreAfterGenerate;
            blackListBox.Items.Clear();

            if (!string.IsNullOrEmpty(cfg.oldPath)) oldVersionName.Text = Path.GetFileName(cfg.oldPath);
            if (!string.IsNullOrEmpty(cfg.newPath)) newVersionName.Text = Path.GetFileName(cfg.newPath);

            for (int i = 0; i < cfg.blackList.Count; i++)
            {
                var relativePath = cfg.blackList.ToList()[i];
                blackListBox.Items.Add(relativePath);
                blackListBox.SetItemChecked(i, true);
            }
            CheckBtnState();
        }

        private void PickOld_Click(object sender, EventArgs e)
        {
            using (BetterFolderBrowser folderBrowser = new BetterFolderBrowser())
            {
                if (Directory.Exists(cfg.oldPath)) folderBrowser.RootFolder = cfg.oldPath;
                if (folderBrowser.ShowDialog() == DialogResult.OK)
                {
                    var tempPath = folderBrowser.SelectedPath;
                    if (string.IsNullOrEmpty(tempPath))
                    {
                        Utils.Error("错误：拾取的路径无效！");
                        oldVersionName.Text = "";
                        return;
                    }
                    else
                    {
                        if (!string.IsNullOrEmpty(cfg.newPath))
                        {
                            if(Path.Equals(tempPath, cfg.newPath))
                            {
                                Utils.Error("错误：新、旧两个目录不得是同一个目录！");
                                return;
                            }
                            else if (Utils.IsPathUnderDirectory(tempPath, cfg.newPath) || Utils.IsPathUnderDirectory(cfg.newPath, tempPath))
                            {
                                Utils.Error("错误：新、旧两个目录不得互相包含！");
                                return;
                            }
                            else if (!UpdateInfo.isVersionValid(Path.GetFileName(tempPath)))
                            {
                                Utils.Error($"目录的命名不符合规范！\n{Path.GetFileName(tempPath)}");
                                return;
                            }
                        }
                        cfg.oldPath = tempPath;
                        oldVersionName.Text = Path.GetFileName(cfg.oldPath);
                        cfg.Save();
                    }
                }
            }
            CheckBtnState();
        }

        private void pickNew_Click(object sender, EventArgs e)
        {
            using (BetterFolderBrowser folderBrowser = new BetterFolderBrowser())
            {
                if (Directory.Exists(cfg.newPath)) folderBrowser.RootFolder = cfg.newPath;
                else if (Directory.Exists(cfg.oldPath)) folderBrowser.RootFolder = cfg.oldPath;
                if (folderBrowser.ShowDialog() == DialogResult.OK)
                {
                    var tempPath = folderBrowser.SelectedPath;
                    if (string.IsNullOrEmpty(tempPath))
                    {
                        MessageBox.Show("错误：拾取的路径无效！");
                        newVersionName.Text = "";
                        return;
                    }
                    else
                    {
                        if (!string.IsNullOrEmpty(cfg.oldPath))
                        {
                            if (Path.Equals(tempPath, cfg.oldPath))
                            {
                                MessageBox.Show("错误：新、旧两个目录不得是同一个目录！");
                                return;
                            }
                            else if (Utils.IsPathUnderDirectory(tempPath, cfg.oldPath) || Utils.IsPathUnderDirectory(cfg.oldPath, tempPath))
                            {
                                MessageBox.Show("错误：新、旧两个目录不得互相包含！");
                                return;
                            }
                            else if (!UpdateInfo.isVersionValid(Path.GetFileName(tempPath)))
                            {
                                Utils.Error($"目录的命名不符合规范！\n{Path.GetFileName(tempPath)}");
                                return;
                            }
                        }
                        cfg.newPath = tempPath;
                        newVersionName.Text = Path.GetFileName(cfg.newPath);
                        cfg.Save();
                    }
                }
            }
            CheckBtnState();
        }

        private void CheckBtnState()
        {
            var isOldFolderValid = UpdateInfo.isVersionValid(oldVersionName.Text);
            var isNewFolderValid = UpdateInfo.isVersionValid(newVersionName.Text);
            if (!isOldFolderValid)
            {
                oldVersionName.Text = "请拾取有效目录";
                cfg.oldPath = "";
            }
            if (!isNewFolderValid)
            {
                newVersionName.Text = "请拾取有效目录";
                cfg.newPath = "";
            }
            bool isEnvValid = isOldFolderValid && isNewFolderValid;
            pickBlackList.Enabled = isEnvValid;
            clearUncheckedList.Enabled = isEnvValid;
            analysis.Enabled = isEnvValid;
            NextVer.Enabled = isNewFolderValid;
        }

        private void pickBlackList_Click(object sender, EventArgs e)
        {
            using (BetterFolderBrowser folderBrowser = new BetterFolderBrowser())
            {
                if (Directory.Exists(cfg.lastBListPath)) folderBrowser.RootFolder = cfg.lastBListPath;
                else folderBrowser.RootFolder = cfg.newPath;

                folderBrowser.Multiselect = true;
                
                if (folderBrowser.ShowDialog() == DialogResult.OK)
                {
                    foreach (var folder in folderBrowser.SelectedFolders)
                    {
                        if (!string.IsNullOrEmpty(folder))
                        {
                            var tempPath = folder;
                            if (Path.Equals(tempPath, cfg.oldPath) || Path.Equals(tempPath, cfg.newPath))
                            {
                                MessageBox.Show("错误：不可将黑名单目录设置为根目录！");
                                return;
                            }
                            bool IsUnderOldFolder = Utils.IsPathUnderDirectory(tempPath, cfg.oldPath);
                            bool isUnderNewFolder = Utils.IsPathUnderDirectory(tempPath, cfg.newPath);
                            bool isInSubFolder = IsUnderOldFolder || isUnderNewFolder;
                            if (isInSubFolder)
                            {
                                string relativePath = "";
                                if (IsUnderOldFolder) relativePath = Utils.GetRelativePath(cfg.oldPath, tempPath);
                                else relativePath = Utils.GetRelativePath(cfg.newPath, tempPath);
                                if (string.IsNullOrEmpty(relativePath))
                                {
                                    MessageBox.Show("错误：无法识别其相对路径！");
                                    return;
                                }
                                else
                                {
                                    if(cfg.blackList.Add(relativePath))
                                    {
                                        blackListBox.Items.Add(relativePath);
                                        blackListBox.SetItemChecked(GetIndexOf(relativePath), true);
                                        cfg.lastBListPath = tempPath;
                                        cfg.Save();
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        private int GetIndexOf(string str)
        {
            return blackListBox.Items.IndexOf(str);
        }

        private void analysis_Click(object sender, EventArgs e)
        {
            UpdateGenerateBtnState(false);
            if (isAnalysis) isAnalysis = false;
            if (activeThread.Count > 0)
            {
                return;
            }

            cfg.temp_Deleted = new HashSet<string>();
            cfg.temp_Add = new HashSet<string>();
            cfg.temp_Modified = new HashSet<string>();

            var files = Directory.GetFiles(cfg.oldPath, "*", SearchOption.AllDirectories);
            foreach (var file in files) relativePaths.Add(Utils.GetRelativePath(cfg.oldPath, file));
            files = Directory.GetFiles(cfg.newPath, "*", SearchOption.AllDirectories);
            foreach (var file in files) relativePaths.Add(Utils.GetRelativePath(cfg.newPath, file));

            relativePaths = relativePaths.Where(
                rp =>
                {
                    foreach (var blist in cfg.blackList)
                    {
                        if (rp.StartsWith(blist)) return false;
                    }
                    return true;
                }).ToHashSet();

            var taskQueue = new Queue<string>();
            //过滤掉免检文件夹下的文件
            foreach (var path in relativePaths)
            {
                taskQueue.Enqueue(path);
            }


            object getTaskLock = new Object();
            object updateProgressBarLock = new Object();
            object threadCompleteLock = new Object();
            int tCount = 16;
            isAnalysis = true;
            for (int i = 0; i < tCount; i++)
            {
                var subThread = new Thread(FileCompare);
                activeThread.Enqueue(subThread);
                subThread.Start();
            }
            analysis.Text = "终止分析进程";

            void FileCompare()
            {
                while (isAnalysis)
                {
                    var relativePath = "";
                    lock (getTaskLock)
                    {
                        if (taskQueue.Count > 0) relativePath = taskQueue.Dequeue();
                        float processedCount = relativePaths.Count - taskQueue.Count;
                        UpdateProgress(processedCount, relativePaths.Count);
                    }
                    if (string.IsNullOrEmpty(relativePath)) break;

                    var oldFilePath = Utils.GetAbsolutePath(cfg.oldPath, relativePath);
                    var newFilePath = Utils.GetAbsolutePath(cfg.newPath, relativePath);
                    var state = Utils.AreFilesDifferent(relativePath, oldFilePath, newFilePath);
                    switch (state)
                    {
                        case FileChangeState.Unchanged: break;
                        case FileChangeState.Added:
                            UpdateListBox(addedFiles, relativePath);
                            //addedFiles.Items.Add(relativePath);
                            cfg.temp_Add.Add(relativePath);
                            break;
                        case FileChangeState.Deleted:
                            UpdateListBox(deletedFiles, relativePath);
                            //deletedFiles.Items.Add(relativePath);
                            cfg.temp_Deleted.Add(relativePath);
                            break;
                        case FileChangeState.Modified:
                            UpdateListBox(modifiedFiles, relativePath);
                            //modifiedFiles.Items.Add(relativePath);
                            cfg.temp_Modified.Add(relativePath);
                            break;
                    }
                }
                lock (threadCompleteLock)
                {
                    activeThread.Dequeue();
                    if (activeThread.Count == 0)
                    {
                        isAnalysis = false;
                        cfg.Save();
                        MessageBox.Show("分析完成");
                        UpdateAnalysisBtn("开始分析");
                        UpdateGenerateBtnState(true);
                    }
                    else UpdateAnalysisBtn("正在回收线程...");
                }
            }
        }

        void UpdateGenerateBtnState(bool active)
        {
            if (generateBtn.InvokeRequired)
            {
                generateBtn.BeginInvoke(new Action<bool>(UpdateGenerateBtnState), active);
            }
            else generateBtn.Enabled = active;
        }

        void UpdateAnalysisBtn(string btnText)
        {
            if (analysis.InvokeRequired)
            {
                analysis.BeginInvoke(new Action<string>(UpdateAnalysisBtn), btnText);
            }
            else analysis.Text = btnText;
        }

        void UpdateListBox(ListBox listBox, string relativePath)
        {
            if (listBox.InvokeRequired)
            {
                listBox.BeginInvoke(new Action<ListBox, string>(UpdateListBox),listBox, relativePath);
            }
            else
            {
                listBox.Items.Add(relativePath);
            }
        }

        void UpdateProgress(float processedCount, float TotalCount)
        {
            var rate = (int)(processedCount / TotalCount * 100);
            var text = $"{progressBar.Value}%[{processedCount}/{TotalCount}]";
            if (progressBar.InvokeRequired || ProgressRate.InvokeRequired)
            {
                progressBar.BeginInvoke(new Action<float>(UpdateProgressBar), rate);
                progressBar.BeginInvoke(new Action<string>(UpdateProgressText), text);
            }
            else
            {
                progressBar.Value = rate;
                ProgressRate.Text = text;
            }
        }

        void UpdateProgressBar(float rate)
        {
            progressBar.Value = (int)rate;
        }

        void UpdateProgressText(string text)
        {
            ProgressRate.Text = text;
        }

        private void GenerateBtn_Click(object sender, EventArgs e)
        {
            using (BetterFolderBrowser folderBrowser = new BetterFolderBrowser())
            {
                folderBrowser.RootFolder = Directory.GetParent(cfg.newPath).FullName;
                if (folderBrowser.ShowDialog() == DialogResult.OK)
                {
                    var outputFolder = folderBrowser.SelectedPath;
                    var dstPath = Path.Combine(outputFolder, newVersionName.Text);
                    if (Directory.Exists(dstPath)) 
                    {
                        var result = MessageBox.Show($"检测到输出目录[{dstPath}]非空，你是否要清理掉该目录并输出？\n" +
                            $"是：清理后输出\n" +
                            $"否：不清理，直接输出\n" +
                            $"取消：不清理也不输出",
                            "输出目录非空警告",
                            MessageBoxButtons.YesNoCancel,
                            MessageBoxIcon.Question);

                        switch (result)
                        {
                            case DialogResult.Yes:
                                var dirs = Directory.GetDirectories(dstPath);
                                foreach (var dir in dirs)
                                {
                                    Directory.Delete(dir, true);
                                }
                                var files = Directory.GetFiles(dstPath);
                                foreach (var file in files)
                                {
                                    File.Delete(file);
                                }
                                break;
                            case DialogResult.No:
                                break;
                            case DialogResult.Cancel:
                                return;
                        }
                    }

                    var delList = new List<string>();
                    foreach (var item in deletedFiles.Items)
                    {
                        delList.Add(item.ToString());
                    }
                    var updateInfo = new UpdateInfo(oldVersionName.Text , newVersionName.Text, delList);
                    if (!Directory.Exists(dstPath)) Directory.CreateDirectory(dstPath);
                    File.WriteAllText(Path.Combine(dstPath, "info.json"), JsonConvert.SerializeObject(updateInfo));

                    HashSet<string> relativePaths = new HashSet<string>();


                    foreach (var item in addedFiles.Items)
                    {
                        relativePaths.Add(item.ToString());
                    }
                    foreach (var item in modifiedFiles.Items)
                    {
                        relativePaths.Add(item.ToString());
                    }

                    foreach (var relativePath in relativePaths)
                    {
                        Utils.CopyFileToDestination(Path.Combine(cfg.newPath, relativePath), Path.Combine(dstPath, relativePath));
                    }

                    try
                    {


                        if (File.Exists(dstPath))
                        {
                            File.Delete(dstPath);
                        }
                        Utils.CreateZip(dstPath, dstPath + ".zip");
                        if (cfg.clearDirectoryAfterGenerate)
                        {
                            Directory.Delete(dstPath, true);
                        }
                    }
                    catch (Exception)
                    {
                        MessageBox.Show("输出Zip时失败！","错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }

                    Utils.SelectFileInExpoler(dstPath + ".zip");
                }
            }
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            cfg.clearDirectoryAfterGenerate = cb_ClearAfterGenerate.Checked;
        }

        private void checkBox1_CheckedChanged_1(object sender, EventArgs e)
        {
            cfg.openExploreAfterGenerate = cb_OpenExplore.Checked;
        }

        private void NextVer_Click(object sender, EventArgs e)
        {
            cfg.oldPath = cfg.newPath;
            cfg.newPath = "";
            oldVersionName.Text = newVersionName.Text;
            newVersionName.Text = "";
            generateBtn.Enabled = false;
            CheckBtnState();
        }
    }
}
